/*
 * Vehicle.cpp
 *
 *  Created on: 14 mei 2013
 *      Author: Wietse
 */

#include "Vehicle.h"
#include "Swarm.h"


/**
 * Destructor: must be declared explicitly even when it is virtual to avoid
 * undefined references to 'vtable
 */
Vehicle::~Vehicle()
{
}

/**
 * Initialise standard settings for quadcopters and basestations
 */
void Vehicle::init()
{
	// Initialize the image of the swarm
	std::fill_n(imageQuadcopters, NUM_OF_QUADCOPTERS, false);
	std::fill_n(imageCrawlers, NUM_OF_CRAWLERS, false);

	// Prepare file for output
	if(OUTPUT && sourceType != UNKNOWN && !(ID > NUM_OF_QUADCOPTERS && ID > NUM_OF_CRAWLERS))
	{
		clearFile();
	}
}

Component Vehicle::getSourceType()
{
	return sourceType;
}

int Vehicle::getID()
{
	return ID;
}

Position* Vehicle::getPosition()
{
	return &position;
}

/**
 * Receive Messages
 */
void Vehicle::receiveMessages(Swarm *swarm)
{
	// Get the messages from the swarm
	std::vector<Message> *messages = swarm->getMessages();

	// Iterate over the messages
	for(std::vector<int>::size_type i = 0; i < messages->size(); i++)
	{
		// Check if the message is in range of the object
		bool inRange = messages->at(i).inRange(&position);

		if(inRange)
		{
			// Get the data object and add it
			Data received = messages->at(i).getData();
			addData(swarm, received, true);
			double distance = Position::sub(&position, received.getStart()).mag();
			//std::cout << "Distance between data and vehicle: " << distance << std::endl;
		}
	}
}

/**
 * Add the data to the receivedData vector if needed
 */
void Vehicle::addData(Swarm *swarm, Data& received, bool resend)
{
	bool add = true;

	// Check if the message is already received by the Vehicle
	for(std::vector<int>::size_type j = 0; j < receivedData.size(); j++)
	{
		// Get data object
		Data old = this->receivedData[j];

		// Compare SourceType and ID and check if the received object is newer
		// then the one in receivedData
		bool sameSourceType = old.getSourceType() == received.getSourceType();
		bool sameID =  old.getSenderID() == received.getSenderID();
		bool newer = old.isNewer(received);

		// Check if the Data is about the same object
		if(sameSourceType && sameID)
		{
			// Check if the Data is newer
			if(newer)
			{
				receivedData.erase(receivedData.begin() + j);
			}
			else
			{
				add = false;
			}
			break;
		}
	}

	// Compare sourceType and ID from received data with this component
	bool fromThis = received.getSourceType() == sourceType && received.getSenderID() == ID;

	// Check if the data has to be added and if it is not about this component
	if(add == true && fromThis == false)
	{
		// Store the message
		receivedData.push_back(received);

		// Update image
		if(received.getSourceType() == QUADCOPTER)
		{
			imageQuadcopters[received.getSenderID() - 1] = true;
		}
		else if(received.getSourceType() == CRAWLER)
		{
			imageCrawlers[received.getSenderID() - 1] = true;
		}

		// Resend the message if needed
		if(resend && sourceType == QUADCOPTER && RANDOM_MESSAGE && rand() % 100 < RESEND_PROBABILITY)
		{
			//std::cout << "Quadcopter " << this->ID  << " passing through message" << std::endl;
			Message msg = Message(sourceType, ID, position, received);
			swarm->addMessage(msg);
		}
	}
}



/*
 ****************************
 * 		Print functions		*
 ****************************
 */

/**
 * Print all the data that has been received by the vehicle to the console
 */
void Vehicle::printData()
{
	// Print header: sourceType, ID and time
	std::cout << "Data received by ";
	printSourceType();
	std::cout << ID << " at ";
	printTime();
	std::cout << std::endl;

	// Iterate over received data
	for(std::vector<int>::size_type i = 0; i < receivedData.size(); i++)
	{
		// Print data
		receivedData[i].printData();
	}
	std::cout << std::endl;
}

/**
 * Print the image vectors of this object to the console
 */
void Vehicle::printImage()
{
	// Print header: sourceType, ID and time
	std::cout << "Image of ";
	printSourceType();
	std::cout << ID << " at ";
	printTime();
	std::cout<< std::endl;

	// Print Quadcopters Image
	std::cout << "Quadcopter image: {";
	for(int i = 0; i < NUM_OF_QUADCOPTERS; i++)
	{
		std::cout << imageQuadcopters[i] << " ";
	}
	std::cout << "}" << std::endl;

	// Print Crawlers Image
	std::cout << "Crawlers image: {";
	for(int i = 0; i < NUM_OF_CRAWLERS; i++)
	{
		std::cout << imageCrawlers[i] << " ";
	}
	std::cout << "}" << std::endl << std::endl;
}

/**
 * Print the sourceType to the console
 */
void Vehicle::printSourceType()
{
	switch(sourceType)
	{
	case 	UNKNOWN:
		std::cout << "Unknown ";
		break;
	case	BASESTATION:
		std::cout << "Basestation ";
		break;
	case	QUADCOPTER:
		std::cout << "Quadcopter ";
		break;
	case	CRAWLER:
		std::cout << "Crawler ";
		break;
	}
}

/**
 * Print the time to the console
 */
void Vehicle::printTime()
{
	// Retrieve current time and print it
	time_t t = time(0);
	struct tm *now = localtime(&t);
	std::cout 	<< now->tm_hour << ":"
			<< now->tm_min << ":"
			<< now->tm_sec << " :";
}


/*
 ****************************
 * 		Output functions	*
 ****************************
 */

/**
 * Clear the output file from old data and prepare it for new data
 */
void Vehicle::clearFile()
{
	// Get filename and identifier
	std::string filename = getFilename();
	std::string identifier = getIdentifier();

	// Open File
	std::ofstream output;
	output.open(filename.c_str(), std::ios::out);

	// Print ID and time
	output << identifier << " at ";
	outputTime(output);
	output << std::endl;

	// Close file
	output.close();
}

/**
 * Write all the received Data to the output file
 */
void Vehicle::outputData()
{
	// Get filename and identifier
	std::string identifier = getIdentifier();
	std::string filename = getFilename();

	// Create the output file
	std::ofstream output;
	output.open(filename.c_str(), std::ios::app);

	// Print header: sourceType, ID and time
	output << "Data received by " << identifier << " at ";
	outputTime(output);
	output << std::endl;

	// Iterate over received data
	for(std::vector<int>::size_type i = 0; i < receivedData.size(); i++)
	{
		// Print data
		receivedData[i].outputData(output);
	}
	output << std::endl;

	// Close File
	output.close();
}

/**
 * Write the image vectors to the output file
 */
void Vehicle::outputImage()
{
	// Get filename and identifier
	std::string identifier = getIdentifier();
	std::string filename = getFilename();

	// Create the output file
	std::ofstream output;
	output.open(filename.c_str(), std::ios::app);

	// Print header:
	output << "Image of " << identifier << " at ";
	outputTime(output);
	output << std::endl;

	// Print Quadcopters Image
	int counter = 0;
	output << "Quadcopter image: {";
	for(int i = 0; i < NUM_OF_QUADCOPTERS; i++)
	{
		output << imageQuadcopters[i] << " ";
		if(imageQuadcopters[i])
		{
			counter++;
		}
	}
	output << "}" << std::endl;
	output << "Quadcopter count: " << counter << std::endl;

	// Print Crawlers Image
	counter = 0;
	output << "Crawlers image: {";
	for(int i = 0; i < NUM_OF_CRAWLERS; i++)
	{
		output << imageCrawlers[i] << " ";
		if(imageCrawlers[i])
		{
			counter++;
		}
	}
	output << "}" << std::endl;
	output << "Crawler count: " << counter << std::endl << std::endl;
}

/**
 * Output the image counters
 */
void Vehicle::outputImageCounter()
{
	// Get filename and identifier
	std::string identifier = getIdentifier();
	std::string filename = getFilename();

	// Create the output file
	std::ofstream output;
	output.open(filename.c_str(), std::ios::app);

	// Print header:
	outputTime(output);
	output << " ";

	// Print Quadcopters Image
	int counter = 0;
	for(int i = 0; i < NUM_OF_QUADCOPTERS; i++)
	{
		if(imageQuadcopters[i])
		{
			counter++;
		}
	}
	output << counter << "; ";

	// Print Crawlers Image
	counter = 0;
	for(int i = 0; i < NUM_OF_CRAWLERS; i++)
	{
		if(imageCrawlers[i])
		{
			counter++;
		}
	}
	output << counter << std::endl;
}

/**
 * Write the timestamp to the output file
 */
void Vehicle::outputTime(std::ofstream& output)
{
	// Retrieve current time and print it
	time_t t = time(0);
	struct tm *now = localtime(&t);

	output 	<< now->tm_hour << ":"
			<< now->tm_min << ":"
			<< now->tm_sec << " ;";
}

/**
 * Generate the filename for this object
 */
std::string Vehicle::getFilename()
{
	std::string filename;
	filename.clear();

	switch(sourceType)
	{
	case 	UNKNOWN:
		filename += "Unknown ";
		break;
	case	BASESTATION:
		filename += "Basestation ";
		break;
	case	QUADCOPTER:
		filename += "Quadcopter ";
		break;
	case	CRAWLER:
		filename += "Crawler ";
		break;
	}
	filename += intToString(ID);
	filename += ".txt";

	return filename;
}

/**
 * Generate the identifier: sourceType and ID
 */
std::string Vehicle::getIdentifier()
{
	std::string identifier;
	identifier.clear();

	switch(sourceType)
	{
	case 	UNKNOWN:
		identifier += "Unknown ";
		break;
	case	BASESTATION:
		identifier += "Basestation ";
		break;
	case	QUADCOPTER:
		identifier += "Quadcopter ";
		break;
	case	CRAWLER:
		identifier += "Crawler ";
		break;
	}
	identifier += intToString(ID);

	return identifier;
}

/**
 * Convert an int to a std::string
 */
std::string Vehicle::intToString(int number)
{
	std::stringstream result;
	result << number;
	return result.str();
}

/**
 * Remove data that is too old from the Vehicle
 */
void	Vehicle::removeData()
{
	for(unsigned int i = 0; i < receivedData.size(); i++)
	{
		if(receivedData.at(i).isExpired(&position))
		{
			std::cout << "Removing expired data" << std::endl;
			if(receivedData[i].getSourceType() == QUADCOPTER)
			{
				imageQuadcopters[receivedData[i].getSenderID()-1] = false;
			}
			else if(receivedData[i].getSourceType() == CRAWLER)
			{
				imageCrawlers[receivedData[i].getSenderID()-1] = false;
			}

			receivedData.erase(receivedData.begin() + i);

		}
	}
}
